/*********************************************************************************************************
 *  ------------------------------------------------------------------------------------------------------
 *  file description
 *  ------------------------------------------------------------------------------------------------------
 *         \file  ramt.c
 *         \unit  ramt
 *        \brief  This is a simple ram test module for C language
 *       \author  Lamdonn
 *      \version  v0.1.0
 *      \license  GPL-2.0
 *    \copyright  Copyright (C) 2023 Lamdonn.
 ********************************************************************************************************/
#include "ramt.h"

#define PRIVATE_INDEX_RESULT_HISTORY        0
#define PRIVATE_INDEX_RESULT_LATEST         1
#define PRIVATE_INDEX_DURATION              2
#define PRIVATE_INDEX_TEST_MODE             3

/** 
 *  \brief Performs a normal read/write test on the RAM
 *  \param[in] ramt: pointer to the RAMT structure
 *  \return 1 if the test passes, 0 if it fails
 */
static uint8_t test_normal(RAMT *ramt)
{
    uint8_t *base = (uint8_t *)(ramt->base); // Cast the base pointer to a uint8_t pointer
    uint32_t i = 0;

    // Write data: set each byte to its index value modulo 256
    for (i = 0; i < ramt->size; i++) 
    {
        base[i] = (uint8_t)(i % 256);  // Write data
    }

    // Read and verify data
    for (i = 0; i < ramt->size; i++) 
    {
        // If the read value does not match the expected value, return 0
        if (base[i] != (uint8_t)(i % 256)) 
        {  
            return 0; // Test failed
        }
    }

    return 1; // Test passed
}

/** 
 *  \brief Test RAM boundary conditions
 *  \param[in] ramt: Pointer to the RAMT structure
 *  \return 1 if the boundary write test passes, 0 if it fails
 */
static uint8_t test_boundary(RAMT *ramt) 
{
    uint8_t *base = (uint8_t *)(ramt->base); // Cast the base pointer to a uint8_t pointer

    // Test writing at the boundaries
    base[0] = 0xAA;               // Write to the start address
    base[ramt->size - 1] = 0xBB; // Write to the end address

    // Check boundary writes
    if (base[0] != 0xAA || base[ramt->size - 1] != 0xBB) 
    {
        return 0;  // Boundary write failed
    }

    return 1;  // Boundary write succeeded
}

/** 
 *  \brief Test RAM random writes and reads
 *  \param[in] ramt: Pointer to the RAMT structure
 *  \param[in] iterations: Number of random write/read iterations
 *  \return 1 if the random write/read test passes, 0 if it fails
 */
static uint8_t test_random(RAMT *ramt, uint32_t iterations) 
{
    uint8_t *base = (uint8_t *)(ramt->base); // Cast the base pointer to a uint8_t pointer
    uint8_t data;
    uint32_t i = 0;
    uint32_t addr = 0;

    // srand((unsigned int)time(NULL));  // Set random seed (uncomment to enable randomness)
    for (i = 0; i < iterations; i++) 
    {
        // Generate random address and data
        addr = rand() % (ramt->size); // Generate a random address within the RAM size
        data = (uint8_t)(rand() % 256); // Generate random data

        base[addr] = data;  // Write random data to the RAM

        // Read and verify the data
        if (base[addr] != data) 
        {
            return 0;  // Data mismatch
        }
    }

    return 1;  // Random write/read test succeeded
}

/** 
 *  \brief Test RAM data pattern
 *  \param[in] ramt: Pointer to the RAMT structure
 *  \param[in] pattern: The data pattern to write to the RAM
 *  \return 1 if the data pattern test passes, 0 if it fails
 */
static uint8_t test_data_pattern(RAMT *ramt, uint8_t pattern) 
{
    uint8_t *base = (uint8_t *)(ramt->base); // Cast the base pointer to a uint8_t pointer
    uint32_t i = 0;

    // Write the specified pattern data to the RAM
    for (i = 0; i < ramt->size; i++) 
    {
        base[i] = pattern;  // Write pattern data
    }

    // Check the pattern data
    for (i = 0; i < ramt->size; i++) 
    {
        if (base[i] != pattern) 
        {
            return 0;  // If data doesn't match, return 0
        }
    }

    return 1;  // Data pattern test passed
}

/** 
 *  \brief Test RAM inversion
 *  \param[in] ramt: Pointer to the RAMT structure
 *  \return 1 if the inversion test passes, 0 if it fails
 */
static uint8_t test_inversion(RAMT *ramt) 
{
    uint8_t *base = (uint8_t *)(ramt->base); // Cast the base pointer to a uint8_t pointer
    uint32_t i = 0;
    uint8_t temp;

    // Initialize data
    for (i = 0; i < ramt->size; i++) 
    {
        base[i] = (uint8_t)(i % 256); // Fill RAM with initial data
    }

    // Invert RAM data
    for (i = 0; i < ramt->size / 2; i++) 
    {
        temp = base[i]; // Temporarily store the value
        base[i] = base[ramt->size - 1 - i]; // Swap values
        base[ramt->size - 1 - i] = temp; // Complete the swap
    }

    // Check if inversion is correct
    for (i = 0; i < ramt->size; i++) 
    {
        if (base[i] != (uint8_t)((ramt->size - 1 - i) % 256)) 
        {
            return 0;  // If data does not match, return 0
        }
    }

    return 1;  // Inversion test passed
}

/** 
 *  \brief Test RAM clearing
 *  \param[in] ramt: Pointer to the RAMT structure
 *  \return 1 if the clearing test passes, 0 if it fails
 */
static uint8_t test_clearing(RAMT *ramt) 
{
    uint8_t *base = (uint8_t *)(ramt->base); // Cast the base pointer to a uint8_t pointer
    uint32_t i = 0;

    // Clear RAM
    for (i = 0; i < ramt->size; i++) 
    {
        base[i] = 0;  // Set all data to 0
    }

    // Check if clearing was successful
    for (i = 0; i < ramt->size; i++) 
    {
        if (base[i] != 0) 
        {
            return 0;  // If any data is not 0, return 0
        }
    }

    return 1;  // Clearing test passed
}

/** 
 *  \brief Test specific data writing and reading in RAM
 *  \param[in] ramt: Pointer to the RAMT structure
 *  \param[in] data: The specific data to write to the RAM
 *  \return 1 if the specific data test passes, 0 if it fails
 */
static uint8_t test_specific_data(RAMT *ramt, uint8_t data) 
{
    uint8_t *base = (uint8_t *)(ramt->base); // Cast the base pointer to a uint8_t pointer
    uint32_t i = 0;

    // Write specific data to RAM
    for (i = 0; i < ramt->size; i++) 
    {
        base[i] = data;  // Write the specified data to each location in RAM
    }

    // Check the specific data
    for (i = 0; i < ramt->size; i++) 
    {
        if (base[i] != data) 
        {
            return 0;  // If any data does not match, return 0
        }
    }

    return 1;  // Specific data test passed
}

int ramt_init(RAMT *ramt) 
{
    // Check if the ramt pointer is valid
    if (!ramt) return 0;

    // Check if the base pointer within the ramt structure is valid
    if (!ramt->base) return 0;

    // Check if the size of RAM is greater than 0
    if (ramt->size == 0) return 0;

    // Initialize the private data to zero
    memset(ramt->private, 0, sizeof(ramt->private));

    return 1;  // Initialization successful
}

int ramt_start(RAMT *ramt, uint32_t mode, uint32_t duration) 
{
    // Check if the ramt pointer is valid
    if (!ramt) return 0;

    // Check if the base pointer within the ramt structure is valid
    if (!ramt->base) return 0;

    // Check if the size of RAM is greater than 0
    if (ramt->size == 0) return 0;

    // Check if mode and duration are valid
    if (mode == 0 || duration == 0) return 0;

    // Set the test mode and duration in the private data
    ramt->private[PRIVATE_INDEX_TEST_MODE] = mode;
    ramt->private[PRIVATE_INDEX_DURATION] = duration;

    return 1;  // Operation successful
}

int ramt_stop(RAMT *ramt) 
{
    // Check if the ramt pointer is valid
    if (!ramt) return 0;

    // Check if the base pointer within the ramt structure is valid
    if (!ramt->base) return 0;

    // Check if the size of RAM is greater than 0
    if (ramt->size == 0) return 0;

    // Reset the test mode and duration in the private data
    ramt->private[PRIVATE_INDEX_TEST_MODE] = 0;
    ramt->private[PRIVATE_INDEX_DURATION] = 0;

    return 1;  // Operation successful
}

uint32_t ramt_result(RAMT *ramt) 
{
    // Check if the ramt pointer is valid
    if (!ramt) return 0xFFFFFFFF;

    // Check if the base pointer within the ramt structure is valid
    if (!ramt->base) return 0xFFFFFFFF;

    // Check if the size of RAM is greater than 0
    if (ramt->size == 0) return 0xFFFFFFFF;

    // Return the historical result from the private data
    return ramt->private[PRIVATE_INDEX_RESULT_HISTORY];
}

uint32_t ramt_result_latest(RAMT *ramt) 
{
    // Check if the ramt pointer is valid
    if (!ramt) return 0xFFFFFFFF;

    // Check if the base pointer within the ramt structure is valid
    if (!ramt->base) return 0xFFFFFFFF;

    // Check if the size of RAM is greater than 0
    if (ramt->size == 0) return 0xFFFFFFFF;

    // Return the latest result from the private data
    return ramt->private[PRIVATE_INDEX_RESULT_LATEST];
}

void ramt_task(RAMT *ramt) 
{
    uint32_t result = 0;  // Variable to store the results of the tests
    uint32_t mode = 0;    // Variable to store the current testing mode

    // Check if the ramt pointer is not NULL
    if (ramt != NULL)
    {
        // Check if the duration is set to a special value or decrements it
        if (ramt->private[PRIVATE_INDEX_DURATION] == 0xFFFFFFFF || 
            (ramt->private[PRIVATE_INDEX_DURATION] > 0 && (ramt->private[PRIVATE_INDEX_DURATION])--))
        {
            // Get the current testing mode
            mode = ramt->private[PRIVATE_INDEX_TEST_MODE];

            // Execute tests based on the current mode
            if (mode & RAMT_MODE_NORMAL)
            {
                if (!test_normal(ramt))
                {
                    result |= RAMT_MODE_NORMAL; // Mark failure for normal test
                }
            }

            if (mode & RAMT_MODE_BOUNDARY)
            {
                if (!test_boundary(ramt))
                {
                    result |= RAMT_MODE_BOUNDARY; // Mark failure for boundary test
                }
            }

            if (mode & RAMT_MODE_PATTERN)
            {
                if (!test_data_pattern(ramt, 0x55))
                {
                    result |= RAMT_MODE_PATTERN; // Mark failure for data pattern test
                }
            }

            if (mode & RAMT_MODE_RANDOM)
            {
                if (!test_random(ramt, 0x55))
                {
                    result |= RAMT_MODE_RANDOM; // Mark failure for random test
                }
            }

            if (mode & RAMT_MODE_INVERSION)
            {
                if (!test_inversion(ramt))
                {
                    result |= RAMT_MODE_INVERSION; // Mark failure for inversion test
                }
            }

            if (mode & RAMT_MODE_CLEARING)
            {
                if (!test_clearing(ramt))
                {
                    result |= RAMT_MODE_CLEARING; // Mark failure for clearing test
                }
            }

            if (mode & RAMT_MODE_SPECIFIC)
            {
                if (!test_specific_data(ramt, 0x55))
                {
                    result |= RAMT_MODE_SPECIFIC; // Mark failure for specific data test
                }
            }

            // Update the latest result and accumulate the historical result
            ramt->private[PRIVATE_INDEX_RESULT_LATEST] = result;
            ramt->private[PRIVATE_INDEX_RESULT_HISTORY] |= result;
        }
    }
}
